#include "RMSDirectX.h"
#include <Mmsystem.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

LPDIRECTDRAW pDD=0;
LPDIRECTDRAWSURFACE pDDSPrimary=0;
LPDIRECTDRAWSURFACE pDDSBack=0;
LPDIRECTDRAWCLIPPER pClipper=0;
DDSURFACEDESC ddsd;
DDOVERLAYFX   ddofx;

bool bRMSDirectYUVDraw = false;

BYTE CLIP_BYTE[1024]; //Byte clip table

int SSE_ENABLED=0;


void SetSSE()
{
	SSE_ENABLED=0;
	_asm{
		MOV		EAX,1
		CPUID	
		TEST	EDX,1 shl 23
		JNE		Processor_supports_the_MMX
	}
	return;

Processor_supports_the_MMX:
	_asm{

		TEST	EDX,1 shl 25
		JNE		Processor_supports_the_SSE
	}
	return;
Processor_supports_the_SSE:
	SSE_ENABLED=1;

}

void ClipTableInit()
{
	int i;
	// init CLIP array
	for (i=-512;i<511;i++) {
		int a=i;
		if (a<0) a=0;if (a>255) a=255;
		CLIP_BYTE[i+512]=a;
	}
}

void InitDirectXGlobals()
{
	pDD = NULL;
	pDDSPrimary = NULL;
	pDDSBack = NULL;
	pClipper = NULL;
}

BOOL CheckSurface()
{
	if(pDDSPrimary)
	{
		if(pDDSPrimary->IsLost() == DDERR_SURFACELOST)
		{
			pDDSPrimary->Restore();
			return FALSE;
		}
	}
	return TRUE;
}

BOOL InitDDraw(HWND hwnd, int W, int H, DWORD fs)
{
	HRESULT hr;
	DDPIXELFORMAT ddPixelFormat;
	
	hr = DirectDrawCreate(NULL, &pDD, NULL);
	if(hr != DD_OK)
	{
//		TRACE0("Unable to create DDraw object\n");
		return FALSE;
	}
	
	if(fs == FS_FULLSCREEN)
	{
		hr = pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
		if(hr != DD_OK)
		{
			pDD->Release();
			pDD = NULL;
//			TRACE0("Unable to set cooperative level\n");
			return FALSE;
		}
		hr = pDD->SetDisplayMode(720, 576, 16);
		if(hr != DD_OK)
		{
			pDD->Release();
			pDD = NULL;
//			TRACE0("Unable to set display mode\n");
			return FALSE;
		}
	}
	else
	{
		hr = pDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);

		if(hr != DD_OK)
		{
			pDD->Release();
			pDD = NULL;
//			TRACE0("Unable to set cooperative level\n");
			return FALSE;
		}
	}

	memset(&ddsd, 0, sizeof(ddsd));

	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

	hr = pDD->CreateSurface(&ddsd, &pDDSPrimary, NULL);
	if(hr != DD_OK)
	{
//		TRACE0("Unable to create primary surface\n");
		pDD->Release();
		pDD = NULL;
		return FALSE;
	}

	memset(&ddPixelFormat,0,sizeof(ddPixelFormat));
	ddPixelFormat.dwFlags = DDPF_FOURCC;
	ddPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
	ddPixelFormat.dwFourCC = mmioFOURCC('Y','U','Y','2');
	ddPixelFormat.dwYUVBitCount=16;

	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS |DDSD_HEIGHT |DDSD_WIDTH |DDSD_PIXELFORMAT|DDSD_PITCH;
	if(fs == FS_FULLSCREEN)
	{
		ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY;
	}
	else
	{
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	}
	ddsd.dwWidth  = W;
	ddsd.dwHeight = H;
	ddsd.lPitch	  = W*2;

	memcpy(&(ddsd.ddpfPixelFormat), &ddPixelFormat, sizeof(DDPIXELFORMAT));

	hr = pDD->CreateSurface(&ddsd, &pDDSBack, NULL);
	if(hr != DD_OK)
	{
		// RGB Back surface
		memset(&ddPixelFormat,0,sizeof(ddPixelFormat));
		ddPixelFormat.dwFlags = DDPF_RGB;
		ddPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
		ddPixelFormat.dwFourCC = BI_RGB;
		ddPixelFormat.dwRGBBitCount=24;
		ddPixelFormat.dwRBitMask = 0x00FF0000;
		ddPixelFormat.dwGBitMask = 0x0000FF00;
		ddPixelFormat.dwBBitMask = 0x000000FF;


		ddsd.dwSize = sizeof(DDSURFACEDESC);
		ddsd.dwFlags = DDSD_CAPS |DDSD_HEIGHT |DDSD_WIDTH |DDSD_PIXELFORMAT|DDSD_PITCH;
		if(fs == FS_FULLSCREEN)
		{
			ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY;
		}
		else
		{
			ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
		}
		ddsd.dwWidth  = W;
		ddsd.dwHeight = H;
		ddsd.lPitch	  = W*3;

		memcpy(&(ddsd.ddpfPixelFormat), &ddPixelFormat, sizeof(DDPIXELFORMAT));

		hr = pDD->CreateSurface(&ddsd, &pDDSBack, NULL);

		if(hr != DD_OK)
		{
//			TRACE0("Unable to create back surface\n");
			pDD->Release();
			pDD = NULL;
			return FALSE;
		}
		else
		{
			bRMSDirectYUVDraw = false;
		}
	}
	else
	{
		bRMSDirectYUVDraw = true;
	}

	if(fs == FS_FULLSCREEN)
	{
		RECT rectOverlay;
		RECT rectClient;
		SetRect(&rectOverlay, 0, 0, W, H);
		SetRect(&rectClient, 0, 0,720,576);
		memset (&ddofx, 0, sizeof(ddofx));
		ddofx.dwSize = sizeof(DDOVERLAYFX);
		pDDSBack->UpdateOverlay(&rectOverlay, pDDSPrimary, &rectClient, DDOVER_SHOW, &ddofx);
	}


	hr = pDD->CreateClipper(0, &pClipper, NULL);
	if(hr != DD_OK)
	{
//		TRACE0("Unable to create clipper\n");
		pDD->Release();
		pDD = NULL;
		return FALSE;
	}

	hr = pClipper->SetHWnd(0, hwnd);
	if(hr != DD_OK)
	{
//		TRACE0("Unable to set hwnd for clipper\n");
		pDD->Release();
		pDD = NULL;
		return FALSE;
	}

	hr = pDDSPrimary->SetClipper(pClipper);
	if(hr != DD_OK)
	{
//		TRACE0("Unable to set clipper for primary surface\n");
		pDD->Release();
		pDD = NULL;
		return FALSE;
	}
	return TRUE;
}

void CloseDDraw()
{
	if(pClipper) 
	{
		pClipper->Release();
		pClipper = NULL;
	}
	if(pDDSBack)
	{
		pDDSBack->Release();
		pDDSBack = NULL;
	}
	if(pDDSPrimary)
	{
		pDDSPrimary->Release();
		pDDSPrimary = NULL;
	}
	if(pDD)
	{
		pDD->Release();
		pDD = NULL;
	}
}

BYTE* lock_surface (LPDIRECTDRAWSURFACE lpdds) 
{
    HRESULT	ddrval;

	ddrval = lpdds->Lock(NULL, &ddsd, DDLOCK_WAIT|DDLOCK_WRITEONLY, NULL);
	if (ddrval==DDERR_SURFACELOST) {
		ddrval = lpdds->Restore();
		if (ddrval != DD_OK) {
//			TRACE ("restore failed %d\n",0);
			exit (-1);
		}
	} else if (ddrval != DD_OK) {
//		TRACE("lock failed %hi\n", ddrval);
		exit (-1);
	}

	return (BYTE*)ddsd.lpSurface;
}

BOOL unlock_surface (LPDIRECTDRAWSURFACE lpdds,unsigned char* surface)
{
    HRESULT	ddrval;

	ddrval = lpdds->Unlock(surface);
	if (ddrval != DD_OK) {
//		TRACE("unlock failed %d\n",0);
		return FALSE;
	}

	return TRUE;
}

UINT RMSDirectYUV(HWND hwnd,
				  BYTE* Y, 
				  BYTE* U,
				  BYTE* V,
				  int W,int H,
				  int pitch,
				  int addField)
{
	BYTE *surface=lock_surface(pDDSBack);
	DWORD *pDst= (DWORD*) surface ;

	DDSURFACEDESC dd;
	dd.dwSize = sizeof(DDSURFACEDESC);
	pDDSBack->GetSurfaceDesc(&dd);
	pitch=dd.lPitch;

	pDst+=addField*(pitch>>2); //skip upper field
	for(int y=0; y<H; y++) {
	int sz=W;
	//------------------ MMX code ----------------------------
SetSSE();
if (SSE_ENABLED) {
	__int64 zer=0x8080808080808080;
	__asm {
		MOV		ECX,sz
		SHR		ECX,4
		MOV		EAX,Y
		MOV		EBX,U
		MOV		EDX,V
		MOVQ    MM7,zer
		
		//PREFETCHNTA [EAX]
		//PREFETCHNTA [EBX]
		//PREFETCHNTA [EDX]
		MOV		EDI,pDst
		//PXOR	MM7,MM7

Exec16:	
		MOVQ	MM2,[EBX]
		//PREFETCHT0 [EAX+32]
		MOVQ	MM3,[EDX]
		//PREFETCHT0 [EBX+32]
		MOVQ	MM4,MM2
		MOVQ	MM0,[EAX]
		//PREFETCHT0 [EDX+32]
		MOVNTQ  [EBX],MM7
		
		
		MOVQ	MM1,[EAX+8]		
		PUNPCKLBW MM2,MM3			// V3 U3 V2 U2 V1 U1 V0 U0
		MOVNTQ  [EDX],MM7
		PUNPCKHBW MM4,MM3			// V7 U7 V6 U6 V5 U5 V4 U4
		MOVNTQ  [EAX],MM7
		MOVQ	MM5,MM0
		PUNPCKLBW MM0,MM2			// V1 Y3 U1 Y2 V0 Y1 U0 Y0
		MOVQ	MM6,MM1
		MOVNTQ	[EDI],MM0
		PUNPCKHBW MM5,MM2			// V3 Y7 U3 Y6 V2 Y5 U2 Y4
		MOVNTQ	[EDI+8],MM5
		PUNPCKLBW MM1,MM4			// V5 YB U5 YA V4 Y9 U4 Y8
		MOVNTQ	[EDI+16],MM1
		PUNPCKHBW MM6,MM4			// V7 YF U7 YE V6 YD U6 YC
		////PREFETCHT0 [EAX+16]
		MOVNTQ	[EDI+24],MM6
		ADD		EDX,8
		MOVNTQ  [EAX+8],MM7
		ADD		EBX,8
		ADD		EDI,32
		ADD		EAX,16
		LOOP 	Exec16
	}
	pDst+=pitch>>2;
	Y+=W;
	U+=W>>1;
	V+=W>>1;
}else{
//------------------ C-code ----------------------------
	sz=sz>>1;
	while(sz--) {
			*pDst++=
				(Y[0])+(((U[0]))<<8)+
				(Y[1]<<16)+(((V[0]))<<24);
			Y+=2;U+=1;V+=1;
	}
	pDst+=(pitch-W*2)>>2;
}
		}
if (SSE_ENABLED) {
	_asm EMMS;

}	unlock_surface (pDDSBack,surface);

	HRESULT hr;
	RECT rcSrc;
	RECT rcDest;
	POINT p;

	CheckSurface();
	p.x = 0; p.y = 0;

	::ClientToScreen(hwnd, &p);
	::GetClientRect(hwnd, &rcDest);
	SetRect(&rcSrc, 0, 0, W, H);

	OffsetRect(&rcDest, p.x, p.y);

	hr = pDDSPrimary->Blt(&rcDest, pDDSBack, &rcSrc, DDBLT_WAIT, NULL);

	if(hr != DD_OK)
	{
//		TRACE0("Unable to blt\n");
		return FALSE;
	}

	return 0;
}

#define CLIP(x) CLIP_BYTE[(int)(x)+512]

#define RADIX 8
#define F2I(a) ((int)(a*(1<<RADIX)))
#define makeRGB(Y, rr, gg, bb, r, g, b) \
	{int yy=F2I(1.164)*((int)(Y) - 16);	\
	r =CLIP((yy + rr)>>RADIX);	\
	g =CLIP((yy - gg)>>RADIX);	\
	b =CLIP((yy + bb)>>RADIX);  }

#ifndef SET_RGB
#define SET_RGB(p,r,g,b) { *(((BYTE*)p))=b;*(((BYTE*)p)+1)=g;*(((BYTE*)p)+2)=r; }
#endif 


UINT RMSDirectRGB(HWND hwnd,
				  BYTE* Y, 
				  BYTE* U,
				  BYTE* V,
				  int W,int H,
				  int pitch,
				  int addField)
{
	BYTE *surface=lock_surface(pDDSBack);
	BYTE *pDst= (BYTE*) surface ;
	//------------------ MMX code ----------------------------
SetSSE();
if (SSE_ENABLED) {
	const DWORD Mask_mm0[2] = {0xFF00FF00,0xFF00FF00};
	const DWORD Mask_mm1[2] = {0x00FF00FF,0x00FF00FF};
	const DWORD Mask_mm2[2] = {0x0000FFFF,0x0000FFFF};
	const DWORD Mask_mm3[2] = {0x00100010,0x00100010};
	const DWORD Mask_mm4[2] = {0x00800080,0x00800080};
	
	DWORD lines=H;
	BYTE *pOut  = pDst;
	while (lines--) {
		int c=W*3;
		while (c>24) 
		{
			_asm
			{
				pushad
				pxor mm7,mm7
				mov edi,[Y]
				movq mm0,[edi]
				movq mm3,mm0
				mov edi,[U]
				mov eax,[edi]
				movd mm1,eax
				punpcklbw mm1,mm7
				movq mm6,mm1
				psllq mm6,8
				por mm1,mm6
				movq mm4,mm1
				mov edi,[V]
				mov eax,[edi]
				movd mm2,eax
				punpcklbw mm2,mm7
				movq mm6,mm2
				psllq mm6,8
				por mm2,mm6
				movq mm5,mm2

				pxor mm7,mm7

				punpcklbw mm0,mm7 ; Y0
				psubsw mm0,[Mask_mm3]
				punpcklbw mm1,mm7 ; U0
				psubsw mm1,[Mask_mm4]
				punpcklbw mm2,mm7 ; V0
				psubsw mm2,[Mask_mm4]

				punpckhbw mm3,mm7 ; Y1
				psubsw mm3,[Mask_mm3]
				punpckhbw mm4,mm7 ; U1
				psubsw mm4,[Mask_mm4]
				punpckhbw mm5,mm7 ; V1
				psubsw mm5,[Mask_mm4]

				movq mm6,mm0
				psraw mm6,3
				paddsw mm0,mm6

				movq mm6,mm3
				psraw mm6,3
				paddsw mm3,mm6

				movq mm6,mm2
				psraw mm6,1
				paddsw mm6,mm2
				paddsw mm6,mm0 ; R0

				movq mm7,mm5
				psraw mm7,1
				paddsw mm7,mm5
				paddsw mm7,mm3 ; R1

				packuswb mm6,mm7 ; R --> mm6

				movq mm7,mm2
				paddsw mm7,mm1
				psraw mm7,1
				psraw mm2,2
				paddsw mm2,mm7
				movq mm7,mm0
				psubsw mm7,mm2

				movq mm2,mm5
				paddsw mm2,mm4
				psraw mm2,1
				psraw mm5,2
				paddsw mm5,mm2
				movq mm2,mm3
				psubsw mm2,mm5

				packuswb mm7,mm2 ; G --> mm7

				paddsw mm1,mm1
				paddsw mm0,mm1 ; B0

				paddsw mm4,mm4
				paddsw mm3,mm4 ; B1

				packuswb mm0,mm3 ; B --> mm0
				
				movq mm1,mm0	            ;B7,B6,B5,B4,B3,B2,B1,B0 -> mm1
				movq mm2,mm6				;R7,R6,R5,R4,R3,R2,R1,R0 -> mm2
				movq mm0,mm7				;G7,G6,G5,G4,G3,G2,G1,G0 -> mm0

				movq mm5,mm0                ;
				movq mm3,mm1                ;
				movq mm4,mm2                ;
				pand mm0,[Mask_mm0]			;G7,00,G5,00, G3,00,G1,00
				pand mm1,[Mask_mm1]			;00,B6,00,B4, 00,B2,00,B0
				por mm1,mm0                 ;G7,B6,G5,B4, G3,B2,G1,B0 -> mm1

				pand mm5,[Mask_mm1]			;00,G6,00,G4, 00,G2,00,G0
				pand mm4,[Mask_mm0]			;R7,00,R5,00, R3,00,R1,00
				por mm5,mm4                 ;R7,G6,R5,G4, R3,G2,R1,G0 -> mm5

				movq mm7,mm1                ;
				punpcklbw mm1,mm5           ;R3,G3,G2,B2, R1,G1,G0,B0 -> mm1
				punpckhbw mm7,mm5           ;R7,G7,G6,B6, R5,G5,G4,B4 -> mm7
				movq mm6,mm2                ;R7,R6,R5,R4, R3,R2,R1,R0 -> mm6
				movq mm0,mm1                ;
				psrlq mm3,8                 ;00,B7,B6,B5, B4,B3,B2,B1 -> mm3
				punpcklbw mm2,mm3           ;B4,R3,B3,R2, B2,R1,B1,R0 -> mm2
				punpckhbw mm6,mm3           ;00,R7,B7,R6, B6,R5,B5,R4 -> mm6
				pand mm6,[Mask_mm2]			;00,00,B7,R6, 00,00,B5,R4 -> mm6
				pand mm2,[Mask_mm2]			;00,00,B3,R2, 00,00,B1,R0 -> mm2
				punpcklwd mm0,mm2           ;00,00,R1,G1, B1,R0,G0,B0
				punpckhwd mm1,mm2           ;00,00,R3,G3, B3,R2,G2,B2
				movq mm5,mm1                ;
				psllq mm5,48                ;
				por mm0,mm5                 ;G2,B2,R1,G1, B1,R0,G0,B0 -> mm0  ;*
				psrlq mm1,16                ;00,00,00,00, R3,G3,B3,R2 -> mm1
				movq mm2,mm7                ;
				punpcklwd mm2,mm6           ;00,00,R5,G5, B5,R4,G4,B4 -> mm2
				punpckldq mm1,mm2           ;B5,R4,G4,B4, R3,G3,B3,R2 -> mm1  ;*
				punpckhwd mm7,mm6           ;00,00,R7,G7, B7,R6,G6,B6 -> mm7
				psllq mm7,16                ;R7,G7,B7,R6, G6,B6,00,00 -> mm7
				psrlq mm2,32                ;
				por mm2,mm7                 ;R7,G7,B7,R6, G6,B6,R5,G5 -> mm2  ;*
				mov	edi,[pOut]
				movntq [edi],mm0
				movntq [edi+8],mm1
				movntq [edi+16],mm2
				emms
				popad
			}
			
			pOut+=24;
		
			Y+=8;U+=4;V+=4;
			c-=24;
		}
		// copy line  to channels ------------------------------
		while (c) {
			
			int y=*Y++;
			int u=*U++;
			int v=*V++;
			
			y-=16;
			u-=128;
			v-=128;
			
			int r	= y+(y>>3)+v+(v>>1);
			int g	= y+(y>>3)-((v+u)>>1)-(v>>2)/*-(u>>3)*/;
			int b	= y+(y>>3)+u+u;
			
			SET_RGB(pOut,CLIP(r),CLIP(g),CLIP(b));
			
			pOut+=3;
			c-=3;

			y=*Y++;
			y-=16;

			r	= y+(y>>3)+v+(v>>1);
			g	= y+(y>>3)-((v+u)>>1)-(v>>2);
			b	= y+(y>>3)+u+u;
			
			SET_RGB(pOut,CLIP(r),CLIP(g),CLIP(b));
			
			pOut+=3;
			c-=3;
		}
	}
}else{
//------------------ C-code ----------------------------
	int x,y;
	for (y=0;y<H;y++){

		#define RGB0 ((BYTE *)pDst)
		#define Y0 ((BYTE *)Y)

		for (x=0;x<(W>>1);x++){
			int r0,g0,b0;
			int r1,g1,b1;

			int u=(int)*U-128;
			int v=(int)*V-128;



			int rr =F2I(1.596)*v;						
			int gg =F2I(0.813)*v + F2I(0.392)*u;
			int bb =F2I(2.017)*u;


			makeRGB(Y0[0],rr,gg,bb,r0,g0,b0);
			makeRGB(Y0[1],rr,gg,bb,r1,g1,b1);

			RGB0[2]=r0;RGB0[1]=g0;RGB0[0]=b0;
			RGB0[5]=r1;RGB0[4]=g1;RGB0[3]=b1;

			// move pointers
			Y+=2;pDst+=6;U++;V++;
		}
	}
}
if (SSE_ENABLED) {
	_asm EMMS;
}
	unlock_surface (pDDSBack,surface);

	HRESULT hr;
	RECT rcSrc;
	RECT rcDest;
	POINT p;

	CheckSurface();

	p.x = 0; p.y = 0;

	::ClientToScreen(hwnd, &p);
	::GetClientRect(hwnd, &rcDest);
	SetRect(&rcSrc, 0, 0, W, H);

	OffsetRect(&rcDest, p.x, p.y);

	hr = pDDSPrimary->Blt(&rcDest, pDDSBack, &rcSrc, DDBLT_WAIT, NULL);

	if(hr != DD_OK)
	{
//		TRACE0("Unable to blt\n");
		return FALSE;
	}

	return 0;
}

void YUVToRGB(	  BYTE* pDst,
				  BYTE* Y, 
				  BYTE* U,
				  BYTE* V,
				  int W,int H,
				  int pitch)
{
	//------------------ MMX code ----------------------------
SetSSE();
if (SSE_ENABLED) {
	const DWORD Mask_mm0[2] = {0xFF00FF00,0xFF00FF00};
	const DWORD Mask_mm1[2] = {0x00FF00FF,0x00FF00FF};
	const DWORD Mask_mm2[2] = {0x0000FFFF,0x0000FFFF};
	const DWORD Mask_mm3[2] = {0x00100010,0x00100010};
	const DWORD Mask_mm4[2] = {0x00800080,0x00800080};
	
	DWORD lines=H;
	BYTE *pOut  = pDst + pitch*(H-1);
	while (lines--) {
		BYTE* pStart = pOut;
		int c=W*3;
		while (c>24) 
		{
			_asm
			{
				pushad
				pxor mm7,mm7
				mov edi,[Y]
				movq mm0,[edi]
				movq mm3,mm0
				mov edi,[U]
				mov eax,[edi]
				movd mm1,eax
				punpcklbw mm1,mm7
				movq mm6,mm1
				psllq mm6,8
				por mm1,mm6
				movq mm4,mm1
				mov edi,[V]
				mov eax,[edi]
				movd mm2,eax
				punpcklbw mm2,mm7
				movq mm6,mm2
				psllq mm6,8
				por mm2,mm6
				movq mm5,mm2

				pxor mm7,mm7

				punpcklbw mm0,mm7 ; Y0
				psubsw mm0,[Mask_mm3]
				punpcklbw mm1,mm7 ; U0
				psubsw mm1,[Mask_mm4]
				punpcklbw mm2,mm7 ; V0
				psubsw mm2,[Mask_mm4]

				punpckhbw mm3,mm7 ; Y1
				psubsw mm3,[Mask_mm3]
				punpckhbw mm4,mm7 ; U1
				psubsw mm4,[Mask_mm4]
				punpckhbw mm5,mm7 ; V1
				psubsw mm5,[Mask_mm4]

				movq mm6,mm0
				psraw mm6,3
				paddsw mm0,mm6

				movq mm6,mm3
				psraw mm6,3
				paddsw mm3,mm6

				movq mm6,mm2
				psraw mm6,1
				paddsw mm6,mm2
				paddsw mm6,mm0 ; R0

				movq mm7,mm5
				psraw mm7,1
				paddsw mm7,mm5
				paddsw mm7,mm3 ; R1

				packuswb mm6,mm7 ; R --> mm6

				movq mm7,mm2
				paddsw mm7,mm1
				psraw mm7,1
				psraw mm2,2
				paddsw mm2,mm7
				movq mm7,mm0
				psubsw mm7,mm2

				movq mm2,mm5
				paddsw mm2,mm4
				psraw mm2,1
				psraw mm5,2
				paddsw mm5,mm2
				movq mm2,mm3
				psubsw mm2,mm5

				packuswb mm7,mm2 ; G --> mm7

				paddsw mm1,mm1
				paddsw mm0,mm1 ; B0

				paddsw mm4,mm4
				paddsw mm3,mm4 ; B1

				packuswb mm0,mm3 ; B --> mm0
				
				movq mm1,mm0	            ;B7,B6,B5,B4,B3,B2,B1,B0 -> mm1
				movq mm2,mm6				;R7,R6,R5,R4,R3,R2,R1,R0 -> mm2
				movq mm0,mm7				;G7,G6,G5,G4,G3,G2,G1,G0 -> mm0

				movq mm5,mm0                ;
				movq mm3,mm1                ;
				movq mm4,mm2                ;
				pand mm0,[Mask_mm0]			;G7,00,G5,00, G3,00,G1,00
				pand mm1,[Mask_mm1]			;00,B6,00,B4, 00,B2,00,B0
				por mm1,mm0                 ;G7,B6,G5,B4, G3,B2,G1,B0 -> mm1

				pand mm5,[Mask_mm1]			;00,G6,00,G4, 00,G2,00,G0
				pand mm4,[Mask_mm0]			;R7,00,R5,00, R3,00,R1,00
				por mm5,mm4                 ;R7,G6,R5,G4, R3,G2,R1,G0 -> mm5

				movq mm7,mm1                ;
				punpcklbw mm1,mm5           ;R3,G3,G2,B2, R1,G1,G0,B0 -> mm1
				punpckhbw mm7,mm5           ;R7,G7,G6,B6, R5,G5,G4,B4 -> mm7
				movq mm6,mm2                ;R7,R6,R5,R4, R3,R2,R1,R0 -> mm6
				movq mm0,mm1                ;
				psrlq mm3,8                 ;00,B7,B6,B5, B4,B3,B2,B1 -> mm3
				punpcklbw mm2,mm3           ;B4,R3,B3,R2, B2,R1,B1,R0 -> mm2
				punpckhbw mm6,mm3           ;00,R7,B7,R6, B6,R5,B5,R4 -> mm6
				pand mm6,[Mask_mm2]			;00,00,B7,R6, 00,00,B5,R4 -> mm6
				pand mm2,[Mask_mm2]			;00,00,B3,R2, 00,00,B1,R0 -> mm2
				punpcklwd mm0,mm2           ;00,00,R1,G1, B1,R0,G0,B0
				punpckhwd mm1,mm2           ;00,00,R3,G3, B3,R2,G2,B2
				movq mm5,mm1                ;
				psllq mm5,48                ;
				por mm0,mm5                 ;G2,B2,R1,G1, B1,R0,G0,B0 -> mm0  ;*
				psrlq mm1,16                ;00,00,00,00, R3,G3,B3,R2 -> mm1
				movq mm2,mm7                ;
				punpcklwd mm2,mm6           ;00,00,R5,G5, B5,R4,G4,B4 -> mm2
				punpckldq mm1,mm2           ;B5,R4,G4,B4, R3,G3,B3,R2 -> mm1  ;*
				punpckhwd mm7,mm6           ;00,00,R7,G7, B7,R6,G6,B6 -> mm7
				psllq mm7,16                ;R7,G7,B7,R6, G6,B6,00,00 -> mm7
				psrlq mm2,32                ;
				por mm2,mm7                 ;R7,G7,B7,R6, G6,B6,R5,G5 -> mm2  ;*
				mov	edi,[pOut]
				movntq [edi],mm0
				movntq [edi+8],mm1
				movntq [edi+16],mm2
				emms
				popad
			}
			
			pOut+=24;
		
			Y+=8;U+=4;V+=4;
			c-=24;
		}
		// copy line  to channels ------------------------------
		while (c) {
			
			int y=*Y++;
			int u=*U++;
			int v=*V++;
			
			y-=16;
			u-=128;
			v-=128;
			
			int r	= y+(y>>3)+v+(v>>1);
			int g	= y+(y>>3)-((v+u)>>1)-(v>>2)/*-(u>>3)*/;
			int b	= y+(y>>3)+u+u;
			
			SET_RGB(pOut,CLIP(r),CLIP(g),CLIP(b));
			
			pOut+=3;
			c-=3;

			y=*Y++;
			y-=16;

			r	= y+(y>>3)+v+(v>>1);
			g	= y+(y>>3)-((v+u)>>1)-(v>>2);
			b	= y+(y>>3)+u+u;
			
			SET_RGB(pOut,CLIP(r),CLIP(g),CLIP(b));
			
			pOut+=3;
			c-=3;
		}
		pOut = pStart - pitch;
	}
}else{
//------------------ C-code ----------------------------
	int x,y;
	pDst += pitch*(H-1);
	for (y=0;y<H;y++){
		BYTE* pStart = pDst;

		#define RGB0 ((BYTE *)pDst)
		#define Y0 ((BYTE *)Y)

		for (x=0;x<(W>>1);x++){
			int r0,g0,b0;
			int r1,g1,b1;

			int u=(int)*U-128;
			int v=(int)*V-128;



			int rr =F2I(1.596)*v;						
			int gg =F2I(0.813)*v + F2I(0.392)*u;
			int bb =F2I(2.017)*u;


			makeRGB(Y0[0],rr,gg,bb,r0,g0,b0);
			makeRGB(Y0[1],rr,gg,bb,r1,g1,b1);

			RGB0[2]=r0;RGB0[1]=g0;RGB0[0]=b0;
			RGB0[5]=r1;RGB0[4]=g1;RGB0[3]=b1;

			// move pointers
			Y+=2;pDst+=6;U++;V++;
		}

		pDst = pStart - pitch;
	}
}
if (SSE_ENABLED) {
	_asm EMMS;

}
}

HRESULT OnPaintDX(HWND hwnd, int W, int H, DWORD fs)
{
	if (pDDSPrimary==0) return 0;
	if(fs == FS_FULLSCREEN)
	{
		return 0;
	}
	HRESULT hr;
	RECT rcDest;
	POINT p;

	CheckSurface();

	p.x = 0; p.y = 0;

	::ClientToScreen(hwnd, &p);
	::GetClientRect(hwnd, &rcDest);
	OffsetRect(&rcDest, p.x, p.y);

	hr = pDDSPrimary->Blt(&rcDest, pDDSBack, NULL, DDBLT_WAIT, NULL);
	
	if(hr != DD_OK)
	{
//		TRACE0("Unable to blt\n");
		return FALSE;
	}

	return 0;
}
